//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Resources;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
using EntLibContrib.Resource.Instrumentation;

namespace EntLibContrib.Resource.Managers
{
	/// <summary>
	/// Wrapper class for the ResourceWriter (sealed) to handle an
	/// IResourceDataNode for consistency between all writers
	/// </summary>
	public sealed class BinaryResourceWriter : IResourceWriter
	{
		#region Field
		private ResourceWriter resourceWriter;
		#endregion
		
		#region Construction
		/// <summary>
		/// Initializes a new instance of the <see cref="BinaryResourceWriter"/> class.
		/// </summary>
		/// <param name="stream">The stream.</param>
		public BinaryResourceWriter(Stream stream)
		{
			if (stream == null)
				throw new ArgumentNullException("stream");
				
			resourceWriter = new ResourceWriter(stream);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="BinaryResourceWriter"/> class.
		/// </summary>
		/// <param name="fileName">Name of the file.</param>
		public BinaryResourceWriter(string fileName)
		{
			if (String.IsNullOrEmpty(fileName))
				throw new ArgumentNullException("fileName");

			resourceWriter = new ResourceWriter(fileName);
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Adds a named resource specified as an <see cref="T:IResourceDataNode"/> to the list of resource to be written.
		/// </summary>
		/// <param name="resourceDataNode">The resource data node.</param>
		/// <exception cref="FileNotFoundException">The file from the file reference was not found</exception>
		/// <remarks>
		/// Binary resources do not support comments or file references.  This method will take a file
		/// referenced in a IResourceFileRef and embed its contents as a simple byte array because it is not
		/// known what the file contains.
		/// </remarks>
		private void AddResource(IResourceDataNode resourceDataNode)
		{
			if (resourceDataNode.FileRef == null)
				resourceWriter.AddResource(resourceDataNode.Name, resourceDataNode.Value);
			else
			{
				FileInfo fileInfo = new FileInfo(resourceDataNode.FileRef.FileName);
				if (!fileInfo.Exists)
					throw new FileNotFoundException(String.Empty, resourceDataNode.FileRef.FileName);

				using(FileStream fileStream = fileInfo.OpenRead())
				{
					try
					{
						int fileLength = checked((int)fileStream.Length);
						byte[] fileBytes = new byte[fileLength];
						fileStream.Read(fileBytes, 0, fileLength);
						resourceWriter.AddResource(resourceDataNode.Name, fileBytes);
					}
					catch (OverflowException e)
					{
						DefaultResourceEventLogger eventLogger = EnterpriseLibraryFactory.BuildUp<DefaultResourceEventLogger>();
						if (eventLogger != null)
							eventLogger.LogError("", e);
					}
				}
			}
		}

		/// <summary>
		/// Adds a named resource specified as an object to the list of resource to be written.
		/// </summary>
		/// <param name="name">The resource name.</param>
		/// <param name="value">The value.</param>
		public void AddResource(string name, object value)
		{
			IResourceDataNode dataNode = value as IResourceDataNode;
			if (dataNode != null)
				this.AddResource(dataNode);
			else	
				resourceWriter.AddResource(name, value);
		}

		/// <summary>
		/// Adds a named resource specified as a string to the list of resource to be written.
		/// </summary>
		/// <param name="name">The resource name.</param>
		/// <param name="value">The value.</param>
		public void AddResource(string name, string value)
		{
			resourceWriter.AddResource(name, value);
		}

		/// <summary>
		/// Adds a named resource specified as a byte array to the list of resource to be written.
		/// </summary>
		/// <param name="name">The resource name.</param>
		/// <param name="value">The value.</param>
		public void AddResource(string name, byte[] value)
		{
			resourceWriter.AddResource(name, value);
		}

		/// <summary>
		/// Adds a unit of data as a resource to the list of resources to be written.
		/// </summary>
		/// <param name="name">The resource name.</param>
		/// <param name="typeName">the resource type name.</param>
		/// <param name="serializedData">The serialized data.</param>
		public void AddResourceData(string name, string typeName, byte[] serializedData)
		{
			resourceWriter.AddResourceData(name, typeName, serializedData);
		}

		/// <summary>
		/// Saves all resources to the output stream in the system default format.
		/// </summary>
		public void Generate()
		{
			resourceWriter.Generate();
		}

		/// <summary>
		/// Saves the resources to the output stream and then closes it.
		/// </summary>
		public void Close()
		{
			resourceWriter.Close();
		}

		/// <summary>
		/// Allow users to save the resources to the output resource file or stream before closing it,
		/// explicitly releasing resources.
		/// </summary>
		public void Dispose()
		{
			resourceWriter.Dispose();
		}
		#endregion
	}
}
